package com.lge.pitchingcoins.ui.server.layer;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.os.Handler;
import android.os.Message;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;

import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.physics.box2d.EdgeShape;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;
import com.lge.pitchingcoins.R;
import com.lge.pitchingcoins.ui.server.ClientScore;
import com.lge.pitchingcoins.util.PitchingCoinsLog;
import com.lge.pitchingcoins.util.Util;
import com.lge.pitchingcoins.util.databases.PitchingCoinsDatabaseHandler;
import com.lge.pitchingcoins.util.sessions.GameCmdManager;
import com.lge.pitchingcoins.util.sessions.GameCmdManager.GameCmdSendDTO;
import com.lge.pitchingcoins.util.sessions.ServerSession;
import com.lge.pitchingcoins.util.sessions.ServiceHelper;

import org.cocos2d.actions.UpdateCallback;
import org.cocos2d.config.ccMacros;
import org.cocos2d.layers.CCLayer;
import org.cocos2d.layers.CCMultiplexLayer;
import org.cocos2d.nodes.CCDirector;
import org.cocos2d.nodes.CCSprite;
import org.cocos2d.types.CGPoint;
import org.cocos2d.types.CGSize;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;

public class GamePlayForServerLayer extends CCLayer implements OnClickListener {
    public static final int CHANGE_IS_GAME_PLAYING = 1000;
    public static final int CONNECTOR_LIST_NOTIFY_DATA_SET_CHANGED = 1001;
    public static final int SCORE_LIST_NOTIFY_DATA_SET_CHANGED = 1002;
    public static final int CONNECTOR_LIST_DIALOG_SHOW = 1003;
    public static final int SCORE_LIST_DIALOG_SHOW = 1004;
    public static final int GAMEOVER_WITH_3SECONDS = 1005;

    public static final float PTM_RATIO = 32.0f;
    public static final float COIN_SIZE = 50.0f;
    

    private Context mContext = null;

    private ServiceHelper serviceHelper = null;
    private ServerSession serverSession = null;

    private ArrayList<ClientScore> clientScoreArrayList = null;

    private Dialog gameStartLayoutDialog = null;
    private AlertDialog gameOverLayoutAlertDialog = null;
    private AlertDialog.Builder gameOverLayoutBuilder = null;

    private Button gameStartButton = null;
    private ListView connectorListView = null;
    private TextView winnerTextView = null;
    private ListView scoreListView = null;

    private ConnectorListAdapter connectorListAdapter = null;
    private ScoreListAdapter scoreListAdapter = null;

    private boolean isGamePlaying = false;
    
    // To Draw
    private World pitchingCoinWorld = null;
    private CGSize winSize = null;
    private float rdelta = 0.0f;

    
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == CHANGE_IS_GAME_PLAYING) {
                Util.setHomeKeyLock((Activity)mContext, isGamePlaying);
            } else if (msg.what == CONNECTOR_LIST_NOTIFY_DATA_SET_CHANGED) {
                if (serverSession.numberOfClients() > 0) {
                    gameStartButton.setEnabled(true);
                } else {
                    gameStartButton.setEnabled(false);
                }

                connectorListAdapter.notifyDataSetChanged();
            } else if (msg.what == SCORE_LIST_NOTIFY_DATA_SET_CHANGED) {
                scoreListAdapter.notifyDataSetChanged();
            } else if (msg.what == CONNECTOR_LIST_DIALOG_SHOW) {
                gameStartLayoutDialog.show();
            } else if (msg.what == SCORE_LIST_DIALOG_SHOW) {
                gameOverLayoutAlertDialog.show();
            } else if (msg.what == GAMEOVER_WITH_3SECONDS) {
                Handler delayHandler = new Handler();
                delayHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        onGameFinished();
                    }
                }, 3000);
            }
        }
    };
    
    private UpdateCallback tickCallback = new UpdateCallback() {
        @Override
        public void update(float delta) {
            tick(delta);
        }
    };


    // Constructors
    public GamePlayForServerLayer(Context context) {
        super();

        mContext = context;
        initComponent();
    }
    
    
    // Getters and Setters
    public boolean isGamePlaying() {
        return isGamePlaying;
    }

    public void setGamePlaying(boolean isGamePlaying) {
        this.isGamePlaying = isGamePlaying;

        mHandler.sendEmptyMessage(CHANGE_IS_GAME_PLAYING);
    }


    // Callback Methods
    @Override
    public void onClick(View view) {
        switch (view.getId()) {
        case R.id.gameStartButton:
            gameStartButtonPressed();
            break;
        }
    }

    @Override
    public void onEnter() {
        super.onEnter();
        
        if (isGamePlaying) {
            
        } else {
            if (serviceHelper != null) {
                serviceHelper.registerService(Util.PC_PORT);
            }

            if (serverSession == null) {
                serverSession = new ServerSession(mContext);
                serverSession.setCallback(this);

                mHandler.sendEmptyMessage(CONNECTOR_LIST_DIALOG_SHOW);
            }
        }
        
        schedule(tickCallback);
    }


    @Override
    public void onExit() {
        super.onExit();
        
        if (isGamePlaying) {
            
        } else {
            if (serviceHelper != null) {
                serviceHelper.tearDown();
            }

            if (serverSession != null) {
                sendMessageTo("IL", ServerSession.toAllString);

                serverSession.tearDown();
                serverSession = null;
            }
        }

        unschedule(tickCallback);
    }

    public void onMessageReceived(String message, String fromNickName) {
        PitchingCoinsLog.d("Server: " + message + "\nFrom: " + fromNickName);

        GameCmdSendDTO gameData = GameCmdManager.parse(message, GameCmdManager.getInstance());
        drawCoins(gameData.getUserName(), gameData.getX(), gameData.getY(), gameData.getZ(), gameData.getForce());
//        drawCoins(fromNickName, 0.0f, 0.0f, 0.0f, 1000);

        
        //        GameCmdManager.pumpUp(mGameCmdProcessor.getCmdList());
        //        sendMessageTo(message, ServerSession.toAllString);
    }

    public void onClientEnteredOrLeave() {
        mHandler.sendEmptyMessage(CONNECTOR_LIST_NOTIFY_DATA_SET_CHANGED);
    }

    public void onGameFinished() {
        setGamePlaying(false);
        sendMessageTo("GO", ServerSession.toAllString);
        
        calculateScore();
        
        // Save data to database
        PitchingCoinsDatabaseHandler mDatabaseHandler = new PitchingCoinsDatabaseHandler(mContext);
        mDatabaseHandler.openDatabase();

        for (ClientScore client : clientScoreArrayList) {
            mDatabaseHandler.insertNewScore(client.getNickname(), client.getScore());
        }

        mDatabaseHandler.closeDatabase();
        
        sortClientScore();
        
     // Set text to winnerTextView for nickname on the first place
//        winnerTextView.setText(clientScoreArrayList.get(0).toString());
        winnerTextView.setText(mContext.getString(R.string.gameoverText));
        
        mHandler.sendEmptyMessage(SCORE_LIST_NOTIFY_DATA_SET_CHANGED);
        mHandler.sendEmptyMessage(SCORE_LIST_DIALOG_SHOW);
        
        clearGame();
    }
    
    @Override
    public boolean ccKeyDown(int keyCode, KeyEvent event) {
        switch (keyCode) {
        case KeyEvent.KEYCODE_BACK:
            if (isGamePlaying) {
                return isGamePlaying;
            }
        }

        return super.ccKeyDown(keyCode, event);
    }


    // Member Methods
    public void initComponent() {
        this.setIsKeyEnabled(true);
        
        serviceHelper = new ServiceHelper(mContext);
        serviceHelper.initializeNsd();

        clientScoreArrayList = new ArrayList<ClientScore>();

        initGameStartLayoutDialog();
        initGameOverLayoutAlertDialog();

        connectorListView = (ListView)gameStartLayoutDialog.findViewById(R.id.connectorListView);
        gameStartButton = (Button)gameStartLayoutDialog.findViewById(R.id.gameStartButton);

        connectorListAdapter = new ConnectorListAdapter(mContext);
        connectorListView.setAdapter(connectorListAdapter);
        gameStartButton.setOnClickListener(this);
        
        CCSprite targetSprite = CCSprite.sprite("target.png");
        addChild(targetSprite);
        
        winSize = CCDirector.sharedDirector().winSize();
        targetSprite.setPosition(winSize.width / 2.0f, winSize.height / 2.0f);
        
        initPitchingCoinWorld();
    }
    
    public void initGameStartLayoutDialog() {
        gameStartLayoutDialog = new Dialog(mContext);
        gameStartLayoutDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        gameStartLayoutDialog.setContentView(R.layout.view_connector_list);
        gameStartLayoutDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
        gameStartLayoutDialog.setOnCancelListener(new OnCancelListener() {			
            @Override
            public void onCancel(DialogInterface dialogInterface) {
                gameStartLayoutDialog.dismiss();
                
                setGamePlaying(false);
                
                ((CCMultiplexLayer)getParent()).setTag(0);
                ((CCMultiplexLayer)getParent()).switchTo(0);
            }
        });
    }

    public void initGameOverLayoutAlertDialog() {
        LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View gameOverLayout = inflater.inflate(R.layout.view_game_over_for_alert_dialog, (ViewGroup)((Activity)mContext).findViewById(R.id.gameOverLayout));

        winnerTextView = (TextView)gameOverLayout.findViewById(R.id.winnerTextView);

        scoreListAdapter = new ScoreListAdapter(mContext);
        scoreListView = (ListView)gameOverLayout.findViewById(R.id.scoreListView);
        scoreListView.setAdapter(scoreListAdapter);
        
//        gameOverLayoutAlertDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);

        gameOverLayoutBuilder = new AlertDialog.Builder(mContext);
        gameOverLayoutBuilder.setView(gameOverLayout);
        gameOverLayoutBuilder.setPositiveButton(R.string.startGameAgainButtonText, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int id) {
                startGameAgainButtonPressed();
            }
        });

        gameOverLayoutBuilder.setNegativeButton(R.string.exitGameButtonText, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int id) {
                exitGameButtonPressed();
            }
        });

        gameOverLayoutAlertDialog = gameOverLayoutBuilder.create();
    }
    
    public void initPitchingCoinWorld() {
        Vector2 gravity = new Vector2(0.0f, 0.0f);
        
        float scaledWidth = winSize.width / PTM_RATIO;
        float scaledHeight = winSize.height / PTM_RATIO;
        
        pitchingCoinWorld = new World(gravity, true);
        pitchingCoinWorld.setContinuousPhysics(true);
        
        BodyDef backgroundBodyDef = new BodyDef();
        backgroundBodyDef.position.set(0.0f, 0.0f);
        
        Body groundBody = pitchingCoinWorld.createBody(backgroundBodyDef);
        EdgeShape groundBox = new EdgeShape();
        
        Vector2 bottomLeft = new Vector2(-2 * COIN_SIZE, -2 * COIN_SIZE);
        Vector2 topLeft = new Vector2(-2 * COIN_SIZE, scaledHeight + 2 * COIN_SIZE);
        Vector2 topRight = new Vector2(scaledWidth + 2 * COIN_SIZE, scaledHeight + 2 * COIN_SIZE);
        Vector2 bottomRight = new Vector2(scaledWidth + 2 * COIN_SIZE, -2 * COIN_SIZE);
        
        groundBox.set(bottomLeft, bottomRight);
        groundBody.createFixture(groundBox, 0);
        
        groundBox.set(topLeft, topRight);
        groundBody.createFixture(groundBox, 0);
        
        groundBox.set(topLeft, bottomLeft);
        groundBody.createFixture(groundBox, 0);
        
        groundBox.set(topRight, bottomRight);
        groundBody.createFixture(groundBox, 0);
    }
    
    public void addNewCoin(CGPoint position, Vector2 velocity, int tag) {
        CCSprite coinSprite = CCSprite.sprite("coin.png");
        coinSprite.setPosition(position);
        coinSprite.setTag(tag);
        addChild(coinSprite);
        
        BodyDef bodyDef = new BodyDef();
        bodyDef.type = BodyType.DynamicBody;
        bodyDef.position.set(position.x / PTM_RATIO, position.y / PTM_RATIO);
        
        PolygonShape coinBox = new PolygonShape();
        coinBox.setAsBox(2.5f, 2.5f);
                
        synchronized (pitchingCoinWorld) {
            Body body = pitchingCoinWorld.createBody(bodyDef);
            body.setUserData(coinSprite);
            
            FixtureDef fixtureDef = new FixtureDef();
            fixtureDef.shape = coinBox;
            fixtureDef.density = 1.0f;
            fixtureDef.friction = 0.5f;
            fixtureDef.restitution = 0.5f;
            body.createFixture(fixtureDef);
            
            body.setLinearVelocity(velocity);
        }
    }
    
    public synchronized void tick(float delta) {
        float FPS = (float)CCDirector.sharedDirector().getAnimationInterval();
        if ((rdelta += delta) < FPS) return;
        
        synchronized (pitchingCoinWorld) {
            pitchingCoinWorld.step(FPS, 8, 1);
        }
        
        rdelta = 0.0f;
        
        Iterator<Body> it = pitchingCoinWorld.getBodies();
        
        while (it.hasNext()) {
            Body b = it.next();
            
            Object userData = b.getUserData();
            
            b.setLinearVelocity(b.getLinearVelocity().x * 0.9f, b.getLinearVelocity().y * 0.9f);
            if (userData != null && userData instanceof CCSprite) {
                final CCSprite sprite = (CCSprite)userData;
                final Vector2 position = b.getPosition();
                sprite.setPosition(position.x * PTM_RATIO, position.y * PTM_RATIO);
                sprite.setRotation(-1.0f * ccMacros.CC_RADIANS_TO_DEGREES(b.getAngle()));
            }
        }
    }
    
    public void calculateScore() {
        Iterator<Body> it = pitchingCoinWorld.getBodies();
        
        while (it.hasNext()) {
            Body body = it.next();
            
            Object userData = body.getUserData();
            
            if (userData != null && userData instanceof CCSprite) {
                final CCSprite sprite = (CCSprite)userData;
                CGPoint spritePosition = CGPoint.ccp(sprite.getPosition().x, sprite.getPosition().y);
                
                int tag = sprite.getTag();
                
                float xCenter = winSize.width / 2.0f;
                float yCenter = winSize.height / 2.0f;
                
                PitchingCoinsLog.d("X Center : " + xCenter + ", Y Center : " + yCenter + ", sprite X : " + spritePosition.x + ", sprite Y : " + spritePosition.y);
                
                int score = (int)((yCenter - Math.sqrt((xCenter - spritePosition.x) * (xCenter - spritePosition.x) + (yCenter - spritePosition.y) * (yCenter - spritePosition.y))) / yCenter * 100.0f);
                PitchingCoinsLog.d("Score of " + tag + " : " + score);
                
                if (score <= 0) {
                    score = 0;
                }
                
                clientScoreArrayList.add(new ClientScore(serverSession.getPlayerNameWithTag(tag), score));
            }
            
            pitchingCoinWorld.destroyBody(body);
        }
    }
    
    public void clearGame() {
        removeAllChildren(true);
        
        CCSprite targetSprite = CCSprite.sprite("target.png");
        addChild(targetSprite);
        
        winSize = CCDirector.sharedDirector().winSize();
        targetSprite.setPosition(winSize.width / 2.0f, winSize.height / 2.0f);
    }

    public void gameStartButtonPressed() {
        setGamePlaying(true);
        sendMessageTo("GS", ServerSession.toAllString);

        clientScoreArrayList.clear();

        gameStartLayoutDialog.dismiss();
        
        // Select who is first.
        String nextPlayer = serverSession.getTurnedPlayerName();
        if (nextPlayer.length() != 0) {
            sendMessageTo("MT", nextPlayer);
        }
    }

    public void startGameAgainButtonPressed() {
        gameOverLayoutAlertDialog.dismiss();

        mHandler.sendEmptyMessage(CONNECTOR_LIST_DIALOG_SHOW);
    }

    public void exitGameButtonPressed() {
        gameOverLayoutAlertDialog.dismiss();

        setGamePlaying(false);
        
        ((CCMultiplexLayer)getParent()).setTag(0);
        ((CCMultiplexLayer)getParent()).switchTo(0);
    }

    public void sendMessageTo(String message, String toNickName) {
        serverSession.sendMessageTo(message, toNickName);
    }
    
    public void drawCoins(String throwingClientName, float x, float y, float z, int force) {
        CGPoint position = CGPoint.ccp(winSize.width / 2.0f, -1 * COIN_SIZE);
        Vector2 velocity = new Vector2(x * force, y * force);
        
        addNewCoin(position, velocity, serverSession.getTagFromPlayerName(throwingClientName));
        
        sendMessageTo("NMT", throwingClientName);
        String nextPlayer = serverSession.getTurnedPlayerName();
        if (nextPlayer.length() != 0) {
            sendMessageTo("MT", nextPlayer);
        } else {
            mHandler.sendEmptyMessage(GAMEOVER_WITH_3SECONDS);
        }
    }
    
    public void sortClientScore() {
        Collections.sort(clientScoreArrayList, new Comparator<ClientScore>() {
            @Override
            public int compare(ClientScore leftHandside, ClientScore rightHandside) {
                return leftHandside.getScore() - rightHandside.getScore();
            }
        });
    }


    // Inner Classes
    class ConnectorListAdapter extends BaseAdapter {
        private Context mContext = null;
        private TextView connectorNameTextView = null;

        public ConnectorListAdapter(Context context) {
            mContext = context;
        }

        @Override
        public int getCount() {
            if (serverSession != null) {
                return serverSession.numberOfClients();
            } else {
                return 0;
            }
        }

        @Override
        public Object getItem(int position) {
            return null;
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View view, ViewGroup viewGroup) {
            View row = view;

            if (row == null) {
                row = LayoutInflater.from(mContext).inflate(R.layout.adapter_server_name_list, null);
            }

            String nicknameToShow = serverSession.getClientInfoListWith(position).getNickname();

            connectorNameTextView = (TextView)row.findViewById(R.id.serverNameTextView);
            connectorNameTextView.setText(nicknameToShow.substring(0, nicknameToShow.length() - 7));

            return row;
        }
    }

    class ScoreListAdapter extends BaseAdapter {
        private Context mContext = null;
        private TextView gradeTextView = null;
        private TextView nickNameTextView = null;
        private TextView scoreTextView = null;


        public ScoreListAdapter(Context context) {
            mContext = context;
        }

        @Override
        public int getCount() {
            return clientScoreArrayList.size();
        }

        @Override
        public Object getItem(int position) {
            return null;
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View view, ViewGroup viewGroup) {
            View row = view;

            if (row == null) {
                row = LayoutInflater.from(mContext).inflate(R.layout.adapter_score_list, null);
            }

            gradeTextView = (TextView)row.findViewById(R.id.gradeTextView);
            nickNameTextView = (TextView)row.findViewById(R.id.nickNameTextView);
            scoreTextView = (TextView)row.findViewById(R.id.scoreTextView);

//            gradeTextView.setText((position + 1) + "");
            gradeTextView.setText("*");
            nickNameTextView.setText(clientScoreArrayList.get(position).getNickname().substring(0, clientScoreArrayList.get(position).getNickname().length() - 7));
            scoreTextView.setText(clientScoreArrayList.get(position).getScore() + "");

            return row;
        }
    }
}